`timescale 1ns/1ps
`default_nettype none

/* NOTE:
* - 根据行号和分区ID计算SDRAM写入地址
*/


/* NOTE:
* 行控:
* - 按照“多开”数量进行分区
* - 先从左到右，再从上到下，顺序分配port id
* - port id按照0,1,2,3,4,...的顺序进行分配
*
* 列控(从上到下）:
* - 按照“折行”数量进行分区
* - 先从左到右，再从上到下，顺序分配port id
* - “多开”总是从8的倍数开始分配port id
*
* 列控(从下到上）:
* - 按照“折行”数量进行分区
* - 先从左到右，再从下到上，顺序分配port id
* - “多开”总是从8的倍数开始分配port id
*/

module row_addr_decoder (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // config
    input  wire [1:0]   I_cfg_scan_mode,
    input  wire [3:0]   I_cfg_sector_count,  // 多开数量
    input  wire [9:0]   I_cfg_sector_height, // 分区高度
    input  wire [1:0]   I_cfg_box_dir,       // 箱体方向
    input  wire [1:0]   I_cfg_block_max,     // 列模式下最大分区id
    input  wire [1:0]   I_cfg_col_loop,      // 列模式读取循环次数(数据组数/8,向上取整)
    // row info
    output wire         O_row_info_req,
    output wire [7:0]   O_row_info_index,
    input  wire [15:0]  I_row_info_data,
    // addr decoder
    input  wire         I_decode_req,     // 行地址译码请求
    input  wire [1:0]   I_decode_buf_sel, // SDRAM按帧分块选择
    input  wire [9:0]   I_decode_row,     // 行号
    input  wire [2:0]   I_decode_sector,  // 分区号
    output wire         O_decode_done,    // 译码完成
    output wire [20:0]  O_decode_addr     // 译码结果
);
//------------------------Parameter----------------------
// fsm
localparam [2:0]
    IDLE = 0,
    DIV  = 1,
    MAP  = 2,
    CALC = 3,
    OVER = 4;

// box direction
localparam [1:0]
    LANDSCAPE = 0, // 横向
    PORTRAIT0 = 1, // 纵向，第一个端口在左侧
    PORTRAIT1 = 2; // 纵向，第一个端口在右侧

//------------------------Local signal-------------------
// fsm
reg  [2:0]  state;
reg  [2:0]  next;

// divider
wire        div_start;
wire [9:0]  div_numer;
wire [9:0]  div_denom;
wire        div_done;
wire [9:0]  div_quotient;
wire [9:0]  div_remain;

// decode
reg         decode_done;
reg  [1:0]  frame_id;
reg  [2:0]  sector_id;
reg  [5:0]  scan_id;
reg  [8:0]  pixel_id;
reg  [8:0]  port_num;
reg  [4:0]  port_id;
reg  [7:0]  col_addr;
reg  [10:0] row_addr;
reg  [1:0]  bank_addr;

//------------------------Instantiation------------------
// serial_divider
serial_divider #(/*{{{*/
    .M          ( 10 ),
    .N          ( 10 ),
    .MODE       ( 0 )
) divider (
    .I_sclk     ( I_sclk ),
    .I_rst_n    ( I_rst_n ),
    .I_start    ( div_start ),
    .I_numer    ( div_numer ),
    .I_denom    ( div_denom ),
    .O_done     ( div_done ),
    .O_quotient ( div_quotient ),
    .O_remain   ( div_remain )
);/*}}}*/

//------------------------Body---------------------------
//{{{+++++++++++++++++++++fsm++++++++++++++++++++++++++++
// state
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        state <= IDLE;
    else
        state <= next;
end

// next
always @(*) begin
    case (state)
        IDLE: begin
            if (I_decode_req)
                next = DIV;
            else
                next = IDLE;
        end

        DIV: begin
            if (!div_done)
                next = DIV;
            else if (I_cfg_box_dir == LANDSCAPE)
                next = MAP;
            else
                next = CALC;
        end

        MAP: begin
            next = OVER;
        end

        CALC: begin
            if (sector_id == 1'b0)
                next = OVER;
            else
                next = CALC;
        end

        OVER: begin
            next = IDLE;
        end

        default: begin
            next = IDLE;
        end
    endcase
end

//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++divider++++++++++++++++++++++++
assign div_start = I_decode_req;
assign div_numer = I_decode_row;
assign div_denom = I_cfg_sector_height;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++row info+++++++++++++++++++++++
assign O_row_info_req   = div_done;
assign O_row_info_index = div_remain[7:0];
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++decode+++++++++++++++++++++++++
assign O_decode_done = decode_done;
assign O_decode_addr = {bank_addr, row_addr, col_addr};

// decode_done
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        decode_done <= 1'b0;
    else if (state == OVER)
        decode_done <= 1'b1;
    else
        decode_done <= 1'b0;
end

// frame_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        frame_id <= 1'b0;
    else if (I_decode_req)
        frame_id <= I_decode_buf_sel;
end

// sector_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        sector_id <= 1'b0;
    else if (I_decode_req)
        sector_id <= I_decode_sector;
    else if (state == CALC)
        sector_id <= sector_id - 1'b1;
end

// scan_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        scan_id <= 1'b0;
    else if (state == MAP)
        scan_id <= {I_row_info_data[10], I_row_info_data[15:11]};
    else if (state == CALC)
        scan_id <= 1'b0;
end

// pixel_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_id <= 1'b0;
    else if (div_done)
        pixel_id <= div_remain[8:0];
    else if (state == MAP)
        pixel_id <= I_row_info_data[8:0];
    else if (state == CALC && sector_id != 1'b0)
        pixel_id <= pixel_id + I_cfg_sector_height[8:0];
end

// port_num
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        port_num <= 1'b0;
    else if (div_done) begin
        if (I_cfg_box_dir == LANDSCAPE)
            port_num <= div_quotient[5:0] * I_cfg_sector_count + sector_id;
        else
            port_num <= div_quotient[1:0]; // 纵向最多四开
    end
end

// port_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        port_id <= 1'b0;
    else if (state == MAP)
        port_id <= port_num[4:0];
    else if (state == CALC) begin
        // NOTE:
        // 正常：0 - 0
        // 对开：0 - 0，1 - 8 or 16
        // 三开：0 - 0, 1 - 8, 2 - 16
        // 四开：0 - 0, 1 - 8, 2 - 16, 3 - 24
        // 只有在对开，且数据组数为9~16组时，
        // port_id = port_num[1:0] * 16
        // 其他时候
        // port_id = port_num[1:0] * 8
        if (I_cfg_block_max == 1'b1 && I_cfg_col_loop == 2'd2)
            port_id <= {port_num[0], 4'd0};
        else 
            port_id <= {port_num[1:0], 3'd0};
    end
end

// col_addr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        col_addr <= 1'b0;
    else if (state == OVER) begin
        if (I_cfg_box_dir == LANDSCAPE)
        begin
            if(I_cfg_scan_mode==2)
                col_addr <= {pixel_id[6:3], port_id[3:2], 2'd0};
            else
                col_addr <= {pixel_id[5:3], port_id[4:2], 2'd0};
        end
        else
        begin
            if(I_cfg_scan_mode==2)
                col_addr <= {pixel_id[6:3], port_id[3  ], pixel_id[2], 2'd0};
            else
                col_addr <= {pixel_id[5:3], port_id[4:3], pixel_id[2], 2'd0};
        end
    end
end

// row_addr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        row_addr <= 1'b0;
    else if (state == OVER)
        case(I_cfg_scan_mode)
            0:  row_addr <= {frame_id, 2'd0, scan_id[3:0], pixel_id[8:6]};
            1:  row_addr <= {frame_id, 2'd0, scan_id[4:0], pixel_id[7:6]};
            2:  row_addr <= {frame_id, 2'd0, scan_id[4:0], pixel_id[8:7]};
        endcase
end

// bank_addr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        bank_addr <= 1'b0;
    else if (state == OVER) begin
        if (I_cfg_box_dir == LANDSCAPE)
            bank_addr <= port_id[1:0];
        else
            bank_addr <= pixel_id[1:0];
    end
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
